﻿using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Globalization;
using System.Linq;
using System.Text;

namespace BlindData.Names
{
	public class Name
	{
		private const string _defaultFormat = "f l";

		#region Properties

		public string First { get; private set; }

		public string FirstInitial
		{
			get { return GetInitial(this.First); }
		}

		public string Middle { get; private set; }

		public string MiddleInitial
		{
			get { return GetInitial(this.Middle); }
		}

		public string Last { get; private set; }

		public string LastInitial
		{
			get { return GetInitial(this.Last); }
		}

		#endregion

		#region Constructors

		public Name(string first, string last)
		{
			Contract.Requires(!String.IsNullOrEmpty(first));
			Contract.Requires(!String.IsNullOrEmpty(last));

			this.First = first;
			this.Last = last;
		}

		public Name(string first, string middle, string last)
			: this(first, last)
		{
			this.Middle = middle;
		}

		#endregion

		#region To Formats

		public string ToFirstLast()
		{
			return this.ToFirstLast(CultureInfo.CurrentCulture);
		}

		public string ToFirstLast(IFormatProvider provider)
		{
			if (String.IsNullOrEmpty(this.First))
			{
				return this.Last;
			}
			else if (String.IsNullOrEmpty(this.Last))
			{
				return this.First;
			}
			return String.Format(provider, "{0} {1}", this.First, this.Last);
		}

		public string ToFirstLastInitial()
		{
			return this.ToFirstLastInitial(CultureInfo.CurrentCulture);
		}

		public string ToFirstLastInitial(IFormatProvider provider)
		{
			if (String.IsNullOrEmpty(this.First))
			{
				return String.Format(provider, "{0}.", this.LastInitial);
			}
			else if (String.IsNullOrEmpty(this.Last))
			{
				return this.First;
			}
			return String.Format(provider, "{0} {1}.", this.First, this.LastInitial);
		}

		public string ToFirstMiddleLast()
		{
			return this.ToFirstMiddleLast(CultureInfo.CurrentCulture);
		}

		public string ToFirstMiddleLast(IFormatProvider provider)
		{
			if (String.IsNullOrEmpty(this.Middle))
			{
				return this.ToFirstLast(provider);
			}
			return String.Format(provider, "{0} {1} {2}", this.First, this.Middle, this.Last);
		}

		public string ToFirstMiddleInitialLast()
		{
			return this.ToFirstMiddleInitialLast(CultureInfo.CurrentCulture);
		}

		public string ToFirstMiddleInitialLast(IFormatProvider provider)
		{
			if (String.IsNullOrEmpty(this.MiddleInitial))
			{
				return this.ToFirstLast(provider);
			}
			return String.Format(provider, "{0} {1}. {2}", this.First, this.MiddleInitial, this.Last);
		}

		public string ToLastFirst()
		{
			return this.ToLastFirst(CultureInfo.CurrentCulture);
		}

		public string ToLastFirst(IFormatProvider provider)
		{
			if (String.IsNullOrEmpty(this.First))
			{
				return this.Last;
			}
			else if (String.IsNullOrEmpty(this.Last))
			{
				return this.First;
			}
			return String.Format(provider, "{0}, {1}", this.Last, this.First);
		}

		public string ToLastFirstInitial()
		{
			return this.ToLastFirstInitial(CultureInfo.CurrentCulture);
		}

		public string ToLastFirstInitial(IFormatProvider provider)
		{
			if (String.IsNullOrEmpty(this.First))
			{
				return this.Last;
			}
			else if (String.IsNullOrEmpty(this.Last))
			{
				return String.Format(provider, "{0}.", this.FirstInitial);
			}
			return String.Format(provider, "{0}, {1}.", this.Last, this.FirstInitial);
		}

		public string ToInitials()
		{
			return this.ToInitials(CultureInfo.CurrentCulture);
		}

		public string ToInitials(IFormatProvider provider)
		{
			return String.Format(provider, "{0}{1}{2}", this.FirstInitial, this.MiddleInitial, this.LastInitial);
		}

		public string ToInitialsWithPeriods()
		{
			return this.ToInitialsWithPeriods(CultureInfo.CurrentCulture);
		}

		public string ToInitialsWithPeriods(IFormatProvider provider)
		{
			var sb = new StringBuilder();
			if (!String.IsNullOrEmpty(this.FirstInitial))
			{
				sb.AppendFormat(provider, "{0}.", this.FirstInitial);
			}
			if (!String.IsNullOrEmpty(this.MiddleInitial))
			{
				sb.AppendFormat(provider, "{0}.", this.MiddleInitial);
			}
			if (!String.IsNullOrEmpty(this.LastInitial))
			{
				sb.AppendFormat(provider, "{0}.", this.LastInitial);
			}
			return sb.ToString();
		}

		#endregion

		#region ToString

		public override string ToString()
		{
			return this.ToFirstLast();
		}

		public string ToString(IFormatProvider provider)
		{
			return this.ToFirstLast(provider);
		}

		public string ToString(string format)
		{
			return this.ToString(format, CultureInfo.CurrentCulture);
		}

		public string ToString(string format, IFormatProvider provider)
		{
			var output = new StringBuilder();

			var adder = 0;
			for (int i = 0; i < format.Length; i += adder)
			{
				var formatChar = format[i];
				switch (formatChar)
				{
					case 'f':
						if (CheckForInitialPattern(format, i))
						{
							output.Append(this.FirstInitial);
							adder = 2;
						}
						else
						{
							output.Append(this.First);
							adder = 1;
						}
						break;
					case 'm':
						if (CheckForInitialPattern(format, i))
						{
							output.Append(this.MiddleInitial);
							adder = 2;
						}
						else
						{
							output.Append(this.Middle);
							adder = 1;
						}
						break;
					case 'l':
						if (CheckForInitialPattern(format, i))
						{
							output.Append(this.LastInitial);
							adder = 2;
						}
						else
						{
							output.Append(this.Last);
							adder = 1;
						}
						break;
					default:
						output.Append(formatChar);
						adder = 1;
						break;
				}
			}

			return output.ToString();
		}

		#endregion

		#region Helper Methods

		private static string GetInitial(string name)
		{
			if (!String.IsNullOrEmpty(name))
			{
				return name.Substring(0, 1);
			}
			return name;
		}

		public bool CheckForInitialPattern(string format, int pos)
		{
			int nextPos = pos + 1;
			return (nextPos < format.Length && format[nextPos] == 'i');
		}

		#endregion
	}
}
